Un tutoriel ggplot2 pour de beaux graphiques en R

Last update: 2022-01-15

Introduction

I don’t care, just show me the content!

Dans ce tutoriel, vous apprendrez à reproduire l’une des visualisations de données les plus célèbres au monde, qui a été développée par Hans Rosling :

Hans Rosling était un professeur de santé internationale et un porte-parole du progrès humain. Par exemple, contrairement à ce que l’on croit généralement, la pauvreté dans le monde a énormément diminué au cours des dernières décennies. La visualisation que vous pouvez voir ci-dessus est l’un des nombreux exemples qui montrent comment la vie sur terre s’est améliorée au cours des dernières décennies. Le nuage de points illustre la relation entre le revenu par habitant et l’espérance de vie en jusqu’en 2007.

L’objectif de ce tutoriel est de reproduire cette célèbre visualisation de données et d’utiliser des techniques plus complexes dans ggplot2 pour ajouter une visualisation dans une visualisation.

Préparation

  • Vous devez charger les package suivants pour exécuter le tutoriel complet : (Pour des raisons pédagogiques et si les participants sautent à n’importe quel graphe, on charge le package nécessaire à côté de {ggplot2} dans la section respective).
# install CRAN packages

library(tidyverse)
library(gapminder)
library(ggthemes)
library(countrycode)
library(ggpp)

Le jeu de données

Tout d’abord, nous avons besoin des données gapminder. Heureusement, il existe un package gapminder dans R pour cela. Comme d’habitude, nous devons d’abord charger tous les packages nécessaires à la visualisation.

library(tidyverse)
library(gapminder)

gapminder_cleaned <- 
  gapminder %>% 
  # filter(year == "2007") %>% 
  mutate(
    pop2 = pop + 1,
    continent = case_when(
      continent == "Oceania" ~ "Asia",
      TRUE ~ as.character(continent)
    ) %>% as.factor %>% 
      fct_relevel("Asia", "Americas", "Europe", "Africa"),
    year = as.integer(year),
    year_label = as.character(year),
    country_label = ifelse(str_detect(country, "France"), "France", NA)
  )

Afficher les données

head(gapminder_cleaned, 10)
## # A tibble: 10 x 9
##    country     continent  year lifeExp      pop gdpPercap     pop2 year_label country_label
##    <fct>       <fct>     <int>   <dbl>    <int>     <dbl>    <dbl> <chr>      <chr>        
##  1 Afghanistan Asia       1952    28.8  8425333      779.  8425334 1952       NA           
##  2 Afghanistan Asia       1957    30.3  9240934      821.  9240935 1957       NA           
##  3 Afghanistan Asia       1962    32.0 10267083      853. 10267084 1962       NA           
##  4 Afghanistan Asia       1967    34.0 11537966      836. 11537967 1967       NA           
##  5 Afghanistan Asia       1972    36.1 13079460      740. 13079461 1972       NA           
##  6 Afghanistan Asia       1977    38.4 14880372      786. 14880373 1977       NA           
##  7 Afghanistan Asia       1982    39.9 12881816      978. 12881817 1982       NA           
##  8 Afghanistan Asia       1987    40.8 13867957      852. 13867958 1987       NA           
##  9 Afghanistan Asia       1992    41.7 16317921      649. 16317922 1992       NA           
## 10 Afghanistan Asia       1997    41.8 22227415      635. 22227416 1997       NA

Le package {ggplot2}

ggplot2 est un système pour créer des graphiques de manière déclarative, basé sur [The Grammar of Graphics] (https://link.springer.com/chapter/10.1007/978-3-642-21551-3_13). Vous fournissez les données, dites à ggplot2 comment faire correspondre les variables à l’esthétique, quelles primitives graphiques utiliser, et il s’occupe des détails.

Un ggplot est construit à partir de quelques éléments de base :

  1. Données : Les données brutes que vous voulez tracer.
  2. Géométries geom_ : Les formes géométriques qui représenteront les données.
  3. Esthétique aes() : Esthétique des objets géométriques et statistiques, comme la position, la couleur, la taille, la forme et la transparence.
  4. Echelles scale_ : Les cartes entre les données et les dimensions esthétiques, telles que la plage de données à la largeur du graphique ou les valeurs des facteurs aux couleurs.
  5. Transformations statistiques stat_ : Résumés statistiques des données, tels que les quantiles, les courbes ajustées, et les sommes.
  6. Système de coordonnées coord_ : La transformation utilisée pour mettre en correspondance les coordonnées des données dans le plan du rectangle de données.
  7. Facettes facet_ : L’arrangement des données dans une grille de parcelles.
  8. Thèmes visuels theme() : Les défauts visuels globaux d’un tracé, tels que le fond, les grilles, les axes, la police de caractères par défaut, les tailles et les couleurs.

💡 Le nombre d’éléments peut varier en fonction de la façon dont vous les regroupez.

Un ggplot par défaut

Tout d’abord, pour pouvoir utiliser les fonctionnalités de {ggplot2}, nous devons charger le package (que nous pouvons également charger via la tidyverse package collection) :

La syntaxe de {ggplot2} est différente de celle du R de base. En accord avec les éléments de base, un ggplot par défaut a besoin de trois choses que vous devez spécifier : les données, l’esthétique, et une géométrie. On commence toujours à définir un objet de traçage en appelant ggplot(data = df) qui indique simplement à {ggplot2} que l’on va travailler avec ces données. Dans la plupart des cas, vous voudrez tracer deux variables, l’une sur l’axe des x et l’autre sur l’axe des y. Il s’agit d’une esthétique de positionnement. Il s’agit d’une esthétique positionnelle et donc nous ajoutons aes(x = var1, y = var2) à l’appel ggplot() (oui, le aes() signifie esthétique). Cependant, il y a aussi des cas où l’on doit spécifier une, voire trois variables ou plus.

💡 On spécifie les données outside aes() et on ajoute les variables auxquelles ggplot associe l’esthétique inside aes().

Ici, nous indexons la variable gdpPercap à la position x et la variable lifeExp à la position y. Plus tard, nous affecterons également des variables à toutes sortes d’autres caractéristiques esthétiques telles que la couleur, la taille et la forme.

  ggplot(data = gapminder_cleaned, aes(x = gdpPercap, y = lifeExp)) 

Hm, seul un panneau est créé lors de l’exécution. Pourquoi ? C’est parce que {ggplot2} ne sait pas comment nous voulons tracer ces données - nous devons encore fournir une géométrie !

ggplot2 vous permet de stocker le ggobject actuel dans une variable de votre choix en l’assignant à une variable, dans notre cas appelée gapminder_plot. Vous pouvez étendre ce ggobjet plus tard en ajoutant d’autres couches, soit en une seule fois, soit en l’assignant à la même ou à une autre variable.

💡 En utilisant des parenthèses lors de l’assignation d’un objet, l’objet sera imprimé immédiatement (au lieu d’écrire g <- ggplot(...) et ensuite g on écrit simplement (g <- ggplot(...)))..

Il y a beaucoup, beaucoup de géométries différentes (appelées geoms parce que chaque fonction commence généralement par geom_) que l’on peut ajouter à un ggplot par défaut (voir ici pour une liste complète) et encore plus fournies par des paquets d’extension (voir ici pour une collection de paquets d’extension). Indiquons à {ggplot2} quel style nous voulons utiliser, par exemple en ajoutant geom_point() pour créer un nuage de points :

gapminder_plot <-
    ggplot(data = gapminder_cleaned, aes(x = gdpPercap, y = lifeExp)) +
  geom_point(aes(size = pop, fill = continent), shape = 21) 
  
gapminder_plot

On peut aussi combiner plusieurs couches géométriques - et c’est là que la magie et le plaisir commence ! On peut aussi ajuster les axes.

Ajuster les axes

Ajustons l’axe “x”

gapminder_plot +
  # geom_point(aes(size = pop, fill = continent), shape = 21) +
  scale_x_log10(breaks = c(500, 1000, 2000, 4000,
                           8000, 16000, 32000, 64000)) 

Ajustons l’axe “y”

gapminder_plot +
  # geom_point(aes(size = pop, fill = continent), shape = 21) +
  scale_x_log10(breaks = c(500, 1000, 2000, 4000,
                           8000, 16000, 32000, 64000)) +
  scale_y_continuous(breaks = seq(0, 90, by = 10)) 

💡 {ggplot2} comprend à la fois color et colour ainsi que la version courte col.

Chaque geom vient avec ses propres propriétés (appelées arguments) et le même argument peut entraîner un changement différent selon le géom que vous utilisez.

gapminder_plot +
  # geom_point(aes(size = pop, fill = continent), shape = 21) +
  scale_x_log10(breaks = c(500, 1000, 2000, 4000,
                           8000, 16000, 32000, 64000)) +
  scale_y_continuous(breaks = seq(0, 90, by = 10)) +
  scale_color_manual(values = c("#F15772", "#7EEB03",
                                "#FBE700", "#54D5E9")) 

Normalisons le diamètre des point en fonction de lapopulation pour une meilleure visualisation.

Maintenant, on retir les légendes pour aérer la figure
gapminder_plot +
  # geom_point(aes(size = pop, color = continent)) +
  # geom_point(aes(size = pop2), color = "black", shape = 21) +
  scale_x_log10(breaks = c(500, 1000, 2000, 4000,
                           8000, 16000, 32000, 64000)) +
  scale_y_continuous(breaks = seq(0, 90, by = 10)) +
  scale_color_manual(values = c("#F15772", "#7EEB03",
                                "#FBE700", "#54D5E9")) +
  scale_size_continuous(range = c(1, 30)) +
  guides(size = FALSE, color = FALSE) 

rajoutons les titres des axes et retirer le fond gris
theme_set(theme_bw()) # Retire le fond gris


gapminder_plot +
  # geom_point(aes(size = pop, color = continent)) +
  # geom_point(aes(size = pop2), color = "black", shape = 21) +
  scale_x_log10(breaks = c(500, 1000, 2000, 4000,
                           8000, 16000, 32000, 64000)) +
  scale_y_continuous(breaks = seq(0, 90, by = 10)) +
  scale_color_manual(values = c("#F15772", "#7EEB03",
                                "#FBE700", "#54D5E9")) +
  scale_size_continuous(range = c(1, 30)) +
  guides(size = FALSE, color = FALSE) +
  labs( x = "Income",    y = "Life expectancy"  ) 

Affichons l’année des données
library(ggpp)

gapminder_plot +
  # geom_point(aes(size = pop, color = continent)) +
  # geom_point(aes(size = pop2), color = "black", shape = 21) +
  scale_x_log10(breaks = c(500, 1000, 2000, 4000,
                           8000, 16000, 32000, 64000)) +
  scale_y_continuous(breaks = seq(0, 90, by = 10)) +
  scale_color_manual(values = c("#F15772", "#7EEB03",
                                "#FBE700", "#54D5E9")) +
  scale_size_continuous(range = c(1, 30)) +
  guides(size = FALSE, color = FALSE) +
  labs( x = "Income",    y = "Life expectancy"  ) +
  geom_text_npc(aes(label = year_label), 
                npcx = "left", npcy = "bottom", 
                size = 20, color = "#999999", alpha = .3,
                family = "Helvetica Neue"
                ) 

Affichons le nom des pays (Ici uniquement la France)
library(ggrepel)

gapminder_plot +
  # geom_point(aes(size = pop, color = continent)) +
  # geom_point(aes(size = pop2), color = "black", shape = 21) +
  scale_x_log10(breaks = c(500, 1000, 2000, 4000,
                           8000, 16000, 32000, 64000)) +
  scale_y_continuous(breaks = seq(0, 90, by = 10)) +
  scale_color_manual(values = c("#F15772", "#7EEB03",
                                "#FBE700", "#54D5E9")) +
  scale_size_continuous(range = c(1, 30)) +
  guides(size = FALSE, color = FALSE) +
  labs( x = "Income",    y = "Life expectancy"  ) +
  geom_text_npc(aes(label = year_label), 
                npcx = "left", npcy = "bottom", 
                size = 20, color = "#999999", alpha = .3,
                family = "Helvetica Neue"
                ) +
  geom_label_repel(aes(label = country_label), show.legend = F,
                   max.overlaps = 200) 

Ajustons le thème du graphique
gapminder_plot +
  # geom_point(aes(size = pop, color = continent)) +
  # geom_point(aes(size = pop2), color = "black", shape = 21) +
  scale_x_log10(breaks = c(500, 1000, 2000, 4000,
                           8000, 16000, 32000, 64000)) +
  scale_y_continuous(breaks = seq(0, 90, by = 10)) +
  scale_color_manual(values = c("#F15772", "#7EEB03",
                                "#FBE700", "#54D5E9")) +
  scale_size_continuous(range = c(1, 30)) +
  guides(size = FALSE, color = FALSE) +
  labs( x = "Income",    y = "Life expectancy"  ) +
  geom_text_npc(aes(label = year_label), 
                npcx = "left", npcy = "bottom", 
                size = 20, color = "#999999", alpha = .3,
                family = "Helvetica Neue"
                ) +
  geom_label_repel(aes(label = country_label), show.legend = F,
                   max.overlaps = 200) +
  theme(plot.margin = margin(25, 25, 10, 25),    
        plot.title = element_text(color = "#22292F", hjust = 0,                              
                                  margin = margin(b = 15),                              
                                  size = 30,                              
                                  face = "bold"
                                  )) 

Rajoutons un titre pour le graphique

`

gapminder_plot +
  # geom_point(aes(size = pop, color = continent)) +
  # geom_point(aes(size = pop2), color = "black", shape = 21) +
  scale_x_log10(breaks = c(500, 1000, 2000, 4000,
                           8000, 16000, 32000, 64000)) +
  scale_y_continuous(breaks = seq(0, 90, by = 10)) +
  scale_color_manual(values = c("#F15772", "#7EEB03",
                                "#FBE700", "#54D5E9")) +
  scale_size_continuous(range = c(1, 30)) +
  guides(size = FALSE, color = FALSE) +
  labs( x = "Income",    y = "Life expectancy"  ) +
  geom_text_npc(aes(label = year_label), 
                npcx = "left", npcy = "bottom", 
                size = 20, color = "#999999", alpha = .3,
                family = "Helvetica Neue"
                ) +
  geom_label_repel(aes(label = country_label), show.legend = F,
                   max.overlaps = 200) +
  theme(plot.margin = margin(25, 25, 10, 25),    
        plot.title = element_text(color = "#22292F", hjust = 0,                              
                                  margin = margin(b = 15),                              
                                  size = 30,                              
                                  face = "bold"
                                  )) +

  labs(title = "Espérence de vie et Revenus") 

Affichons la légende sous forme d’une carte du monde en bas à droite et retirons ma légende de gauche
library(countrycode)
library(ggthemes)

world <- 
  map_data("world") %>%
  filter(region != "Antarctica") %>% 
  mutate(
    continent = countrycode(sourcevar = region,
                            origin = "country.name",
                            destination = "continent"),
    continent = case_when(
      continent == "Oceania" ~ "Asia",
      TRUE ~ as.character(continent)
    ) %>% as.factor %>% 
      fct_relevel("Asia", "Americas", "Europe", "Africa")
  ) %>% 
  drop_na(continent)

head(world, 10)
##         long      lat group order region subregion continent
## 1  -69.89912 12.45200     1     1  Aruba      <NA>  Americas
## 2  -69.89571 12.42300     1     2  Aruba      <NA>  Americas
## 3  -69.94219 12.43853     1     3  Aruba      <NA>  Americas
## 4  -70.00415 12.50049     1     4  Aruba      <NA>  Americas
## 5  -70.06612 12.54697     1     5  Aruba      <NA>  Americas
## 6  -70.05088 12.59707     1     6  Aruba      <NA>  Americas
## 7  -70.03511 12.61411     1     7  Aruba      <NA>  Americas
## 8  -69.97314 12.56763     1     8  Aruba      <NA>  Americas
## 9  -69.91181 12.48047     1     9  Aruba      <NA>  Americas
## 10 -69.89912 12.45200     1    10  Aruba      <NA>  Americas
continent_map <- 
    ggplot(data = world) +   
      geom_map(map = world,
               aes(long, lat, 
                   group = group, 
                   map_id = region,
                   fill = continent)) +
     theme_map() +
     # coord_map(xlim = c(-180, 180),
     #           ylim = c(-200, 200)) +
     scale_fill_manual(values = c("#F15772", "#7EEB03", "#FBE700", "#54D5E9")) +  
    guides(fill = FALSE) +  
    theme(plot.background = element_blank())

gapminder_plot +
  # geom_point(aes(size = pop, color = continent), show.legend = F) +
  # geom_point(aes(size = pop2), color = "black", shape = 21, show.legend = F) +
  scale_x_log10(breaks = c(500, 1000, 2000, 4000,
                           8000, 16000, 32000, 64000)) +
  scale_y_continuous(breaks = seq(0, 90, by = 10)) +
  scale_color_manual(values = c("#F15772", "#7EEB03",
                                "#FBE700", "#54D5E9")) +
  scale_size_continuous(range = c(1, 30)) +
  guides(size = FALSE, color = FALSE) +
  labs( x = "Income",    y = "Life expectancy"  ) +
  geom_text_npc(aes(label = year_label), 
                npcx = "left", npcy = "bottom", 
                size = 20, color = "#999999", alpha = .3,
                family = "Helvetica Neue"
                ) +
  geom_label_repel(aes(label = country_label), show.legend = F,
                   max.overlaps = 200) +
  theme(plot.margin = margin(25, 25, 10, 25),    
        plot.title = element_text(color = "#22292F", hjust = 0,                              
                                  margin = margin(b = 15),                              
                                  size = 30,                              
                                  face = "bold"
                                  ),
        legend.position = "none"
        ) +

  labs(title = "Espérence de vie et Revenus") +
  annotate("plot_npc", npcx = "right", npcy = "bottom", label = continent_map) 

Affichons chaque continent sur un graphe
library(gganimate)

gapminder_plot +
  geom_point(aes(size = pop, color = continent)) +
  geom_point(aes(size = pop2), color = "black", shape = 21) +
  scale_x_log10(breaks = c(500, 1000, 2000, 4000,
                           8000, 16000, 32000, 64000)) +
  scale_y_continuous(breaks = seq(0, 90, by = 10)) +
  scale_color_manual(values = c("#F15772", "#7EEB03",
                                "#FBE700", "#54D5E9")) +
  scale_size_continuous(range = c(1, 30)) +
  guides(size = FALSE, color = FALSE) +
  labs( x = "Income",    y = "Life expectancy"  ) +
  geom_text_npc(aes(label = year_label), 
                npcx = "left", npcy = "bottom", 
                size = 20, color = "#999999", alpha = .3,
                family = "Helvetica Neue"
                ) +
  geom_label_repel(aes(label = country_label), show.legend = F,
                   max.overlaps = 200) +
 theme(plot.margin = margin(25, 25, 10, 25),    
        plot.title = element_text(color = "#22292F", hjust = 0,                              
                                  margin = margin(b = 15),                              
                                  size = 30,                              
                                  face = "bold"
                                  ),
        legend.position = "none"
        ) +

  labs(title = "Espérence de vie et Revenus") +
  annotate("plot_npc", npcx = "right", npcy = "bottom", label = continent_map) +
  facet_wrap(~ continent)

Et si on rajoutait un peu d’animation!
library(gganimate)


final_result <-
  gapminder_plot +
  geom_point(aes(size = pop, color = continent)) +
  geom_point(aes(size = pop2), color = "black", shape = 21) +
  scale_x_log10(breaks = c(500, 1000, 2000, 4000,
                           8000, 16000, 32000, 64000)) +
  scale_y_continuous(breaks = seq(0, 90, by = 10)) +
  scale_color_manual(values = c("#F15772", "#7EEB03",
                                "#FBE700", "#54D5E9")) +
  scale_size_continuous(range = c(1, 30)) +
  guides(size = FALSE, color = FALSE) +
  labs( x = "Income",    y = "Life expectancy"  ) +
  geom_text_npc(aes(label = year_label), 
                npcx = "left", npcy = "bottom", 
                size = 20, color = "#999999", alpha = .3,
                family = "Helvetica Neue"
                ) +
  geom_label_repel(aes(label = country_label), show.legend = F,
                   max.overlaps = 200) +
  theme(plot.margin = margin(25, 25, 10, 25),    
        plot.title = element_text(color = "#22292F", hjust = 0,                              
                                  margin = margin(b = 15),                              
                                  size = 30,                              
                                  face = "bold"
                                  ),
        legend.position = "none"
        ) +

  labs(title = "Espérence de vie et Revenus") +
  annotate("plot_npc", npcx = "right", npcy = "bottom", label = continent_map) +
  gganimate::transition_time(year) +
  gganimate::exit_fade()

final_result

On peut modifier certaines propriétés pour les deux titres d’axe et d’autres uniquement pour l’un d’entre eux ou des propriétés pour chacun d’entre eux :

Enregistrer le résultat final
library(gganimate)

anim_save("final_result.gif",
          # path = "/",
          res = 200, 
          animation = final_result,
          # fps = 25
          # fps = 10,
          width = 800, 
          height = 700
)
Ressources supplémentaires
## R version 4.0.3 (2020-10-10)
## Platform: x86_64-apple-darwin17.0 (64-bit)
## Running under: macOS Big Sur 10.16
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRblas.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] gganimate_1.0.7   ggthemes_4.2.0    countrycode_1.3.0 ggrepel_0.9.1     ggpp_0.4.3        gapminder_0.3.0   forcats_0.5.1     stringr_1.4.0     dplyr_1.0.6       purrr_0.3.4       readr_1.4.0       tidyr_1.1.2       tibble_3.0.4      ggplot2_3.3.5     tidyverse_1.3.0  
## 
## loaded via a namespace (and not attached):
##  [1] httr_1.4.2        sass_0.4.0        maps_3.3.0        jsonlite_1.7.2    showtext_0.9-4    modelr_0.1.8      bslib_0.2.5.1     assertthat_0.2.1  highr_0.8         showtextdb_3.0    cellranger_1.1.0  yaml_2.2.1        progress_1.2.2    pillar_1.6.1      backports_1.2.1   glue_1.4.2        digest_0.6.27     rvest_0.3.6       colorspace_2.0-0  plyr_1.8.6        htmltools_0.5.1.1 pkgconfig_2.0.3   broom_0.7.3       gifski_0.8.6      haven_2.3.1       bookdown_0.24     sysfonts_0.8.5    scales_1.1.1      tweenr_1.0.1      generics_0.1.0    farver_2.0.3      ellipsis_0.3.2    withr_2.4.2       cli_3.1.0         magrittr_2.0.1    crayon_1.3.4      readxl_1.3.1      evaluate_0.14    
## [39] fs_1.5.0          fansi_0.4.1       xml2_1.3.2        tools_4.0.3       prettyunits_1.1.1 hms_0.5.3         lifecycle_1.0.0   munsell_0.5.0     reprex_0.3.0      compiler_4.0.3    jquerylib_0.1.4   rlang_0.4.12      grid_4.0.3        rstudioapi_0.13   labeling_0.4.2    rmarkdown_2.11    gtable_0.3.0      DBI_1.1.0         R6_2.5.0          lubridate_1.8.0   knitr_1.36        utf8_1.1.4        stringi_1.5.3     rmdformats_1.0.3  Rcpp_1.0.7        vctrs_0.3.8       dbplyr_2.0.0      tidyselect_1.1.0  xfun_0.26